﻿Imports System.Windows.Forms
Imports System
Imports Microsoft.VisualBasic
Imports System.Drawing
Imports Zytonic_Hotkeys.ItemLibrary
Imports System.Reflection
Imports System.Text.RegularExpressions

Public Class KeyMap
    Delegate Sub KeyDelegate(ByVal Keycode As Keys, ByVal KeyReturn As String, ByVal ControlDown As Boolean, ByVal ShiftDown As Boolean)
    Public HotkeyDB As New ItemDB(My.Computer.FileSystem.SpecialDirectories.Temp & "\" & Regex.Replace(System.IO.Path.GetFileNameWithoutExtension(Application.ExecutablePath), "[\\\/:\*\?""'<>|]", vbLf).Trim & "_Hotkeys.cfg")
    Public DisabledKeys As New ItemDB(My.Computer.FileSystem.SpecialDirectories.Temp & "\" & Regex.Replace(System.IO.Path.GetFileNameWithoutExtension(Application.ExecutablePath), "[\\\/:\*\?""'<>|]", vbLf).Trim & "_Disabled.cfg")
    Public Event KB_KeyDown As KeyDelegate
    Public Event KB_KeyUp As KeyDelegate
    Public WithEvents kb_hook As New KeyboardLowLevelHook
    Dim ShiftWasDown As Boolean = False
    Dim WithEvents DetailForm As frmDetail
#Region "Properties"
    Private _RegisteredMethods As New Hashtable
    Private _DisableHotkeyHandles As Boolean = True
    Property DisableHotkeyHandles() As Boolean
        Get
            Return _DisableHotkeyHandles
        End Get
        Set(ByVal v As Boolean)
            _DisableHotkeyHandles = v
        End Set
    End Property
    Private _ControlDown As Boolean = False
    Property ControlDown() As Boolean
        Get
            Return _ControlDown
        End Get
        Set(ByVal v As Boolean)
            _ControlDown = v
        End Set
    End Property
    Private _NumpadVisible As Boolean = True
    Property NumpadVisible() As Boolean
        Get
            Return _NumpadVisible
        End Get
        Set(ByVal v As Boolean)
            _NumpadVisible = v
            If _NumpadVisible = True Then
                TableLayoutPanel23.ColumnStyles.Item(1).Width = 19.04
            Else
                TableLayoutPanel23.ColumnStyles.Item(1).Width = 0
            End If
        End Set
    End Property
    Private _BorderColor As Color = Color.FromArgb(90, 0, 150, 255)
    Property BorderColor() As Color
        Get
            Return _BorderColor
        End Get
        Set(ByVal v As Color)
            _BorderColor = v
            Try
                Dim ctl As Control = Me.GetNextControl(Me, True)
                Do Until ctl Is Nothing
                    Try
                        ctl = Me.GetNextControl(ctl, True)
                        Dim Key As KeyControl = DirectCast(ctl, KeyControl)
                        Key.BorderColor = Me.BorderColor
                    Catch ex As Exception

                    End Try
                Loop
            Catch ex As Exception
            End Try
            Invalidate()
        End Set
    End Property
    Private _TextColor As Color = Color.Black
    Property TextColor() As Color
        Get
            Return _TextColor
        End Get
        Set(ByVal v As Color)
            _TextColor = v
            Try
                Dim ctl As Control = Me.GetNextControl(Me, True)
                Do Until ctl Is Nothing
                    Try
                        ctl = Me.GetNextControl(ctl, True)
                        Dim Key As KeyControl = DirectCast(ctl, KeyControl)
                        Key.TextColor = Me.TextColor
                    Catch ex As Exception

                    End Try
                Loop
            Catch ex As Exception
            End Try
            Invalidate()
        End Set
    End Property
    Private _TextDownColor As Color = Color.Black
    Property TextDownColor() As Color
        Get
            Return _TextDownColor
        End Get
        Set(ByVal v As Color)
            _TextDownColor = v
            Try
                Dim ctl As Control = Me.GetNextControl(Me, True)
                Do Until ctl Is Nothing
                    Try
                        ctl = Me.GetNextControl(ctl, True)
                        Dim Key As KeyControl = DirectCast(ctl, KeyControl)
                        Key.TextDownColor = Me.TextDownColor
                    Catch ex As Exception

                    End Try
                Loop
            Catch ex As Exception
            End Try
            Invalidate()
        End Set
    End Property
    Private _TextOverBackColor As Color = Color.FromArgb(175, 175, 175)
    Property TextOverBackColor() As Color
        Get
            Return _TextOverBackColor
        End Get
        Set(ByVal v As Color)
            _TextOverBackColor = v
            Try
                Dim ctl As Control = Me.GetNextControl(Me, True)
                Do Until ctl Is Nothing
                    Try
                        ctl = Me.GetNextControl(ctl, True)
                        Dim Key As KeyControl = DirectCast(ctl, KeyControl)
                        Key.TextOverBackColor = Me.TextOverBackColor
                    Catch ex As Exception

                    End Try
                Loop
            Catch ex As Exception
            End Try
            Invalidate()
        End Set
    End Property
    Private _TextDownBackColor As Color = Color.FromArgb(DisplayOpacity, 0, 150, 255)
    Property TextDownBackColor() As Color
        Get
            Return _TextDownBackColor
        End Get
        Set(ByVal v As Color)
            _TextDownBackColor = v
            Try
                Dim ctl As Control = Me.GetNextControl(Me, True)
                Do Until ctl Is Nothing
                    Try
                        ctl = Me.GetNextControl(ctl, True)
                        Dim Key As KeyControl = DirectCast(ctl, KeyControl)
                        Key.TextDownBackColor = Me.TextDownBackColor
                    Catch ex As Exception

                    End Try
                Loop
            Catch ex As Exception
            End Try
            Invalidate()
        End Set
    End Property
    Private _DisplayOpacity As Integer = 90
    Private Property DisplayOpacity() As Integer
        Get
            Return _DisplayOpacity
        End Get
        Set(ByVal v As Integer)
            _DisplayOpacity = v
            TextDownBackColor = Color.FromArgb(DisplayOpacity, TextDownBackColor.R, TextDownBackColor.G, TextDownBackColor.B)
            Invalidate()
        End Set
    End Property
#End Region
#Region "Functions"
    <System.Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
    Private Shared Function keybd_event(ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As UInt32, ByVal dwExtraInfo As UIntPtr) As Boolean
    End Function
    Private Sub InvokeKeydown(ByVal key As Keys)
        Dim vk As Byte = CType(key, Byte)
        keybd_event(vk, 0, 0, UIntPtr.Zero)
    End Sub
    Private Sub InvokeKeyup(ByVal key As Keys)
        Dim vk As Byte = CType(key, Byte)
        keybd_event(vk, 0, 2, UIntPtr.Zero)
    End Sub
    Private Sub SetToggles()
        If Me.InvokeRequired = True Then
            Threading.Thread.Sleep(100)
            Me.Invoke(New MethodInvoker(AddressOf SetToggles))
        Else
            If Control.IsKeyLocked(Keys.CapsLock) Then
                SetCapsLock(True)
            Else
                SetCapsLock(False)
            End If
            If Control.IsKeyLocked(Keys.NumLock) Then
                SetNumLock(True)
            Else
                SetNumLock(False)
            End If
        End If
    End Sub
    Private Sub SetCapsLock(ByVal CapsOn As Boolean)
        If CapsOn = True Then
            Key_CapsLock.Toggled = True
            Try
                Dim contrl As Control = Me.GetNextControl(Me, True)
                Do Until contrl Is Nothing
                    Try
                        contrl = Me.GetNextControl(contrl, True)
                        Dim Key As KeyControl = DirectCast(contrl, KeyControl)
                        Key.CapsDown = True
                        Key.Invalidate()
                    Catch ex As Exception
                    End Try
                Loop
            Catch ex As Exception
            End Try
        Else
            Key_CapsLock.Toggled = False
            Try
                Dim contrl As Control = Me.GetNextControl(Me, True)
                Do Until contrl Is Nothing
                    Try
                        contrl = Me.GetNextControl(contrl, True)
                        Dim Key As KeyControl = DirectCast(contrl, KeyControl)
                        Key.CapsDown = False
                        Key.Invalidate()
                    Catch ex As Exception
                    End Try
                Loop
            Catch ex As Exception
            End Try
        End If
    End Sub
    Private Sub SetNumLock(ByVal NumOn As Boolean)
        If NumOn = True Then
            Key_NumLock.Toggled = True
        Else
            Key_NumLock.Toggled = False
        End If
    End Sub
    Public Sub RegisterMethod(ByVal MethodName As String, ByVal Sender As Object)
        _RegisteredMethods.Add(MethodName, Sender)
    End Sub
    Public Sub Execute(ByVal obj As Object, ByVal methodName As String, ByVal params() As Object)
        Try
            Dim mi As MethodInfo = obj.GetType().GetMethod(methodName)
            mi.Invoke(obj, params)
        Catch ex As Exception
        End Try
    End Sub
#End Region
    Private Sub KeyMap_Load(sender As Object, e As EventArgs) Handles Me.Load
        If Control.IsKeyLocked(Keys.CapsLock) Then
            SetCapsLock(True)
        Else
            SetCapsLock(False)
        End If
        If Control.IsKeyLocked(Keys.NumLock) Then
            SetNumLock(True)
        Else
            SetNumLock(False)
        End If
        Try
            Dim contrl As Control = Me.GetNextControl(Me, True)
            Do Until contrl Is Nothing
                Try
                    contrl = Me.GetNextControl(contrl, True)
                    Dim Key As KeyControl = DirectCast(contrl, KeyControl)
                    If DisabledKeys.SearchItem(Key.Tag).Parameters(0) = "True" Then Key.DisableHandle = True
                    AddHandler Key.MouseDown, AddressOf KeyControl_MouseDown
                    AddHandler Key.MouseUp, AddressOf KeyControl_MouseUp
                Catch ex As Exception
                End Try
            Loop
        Catch ex As Exception
        End Try
    End Sub
    Private Sub kb_hook_KeyDown(sender As Object, e As Windows.Forms.KeyEventArgs) Handles kb_hook.KeyDown
        Dim eCtrlDown As Boolean
        Dim eShiftDown As Boolean
        Dim eKeyName As String = Nothing
        Dim eKeyCode As Keys = e.KeyCode

        e.Handled = DisableHotkeyHandles

        Dim KeyPressed As Boolean = False
        If e.Control = True Then
            ControlDown = True
            eCtrlDown = True
            KeyPressed = True
        Else
            ControlDown = False
            eCtrlDown = False
        End If
        Try
            Dim ctl As Control = Me.GetNextControl(Me, True)
            Do Until ctl Is Nothing
                Try
                    ctl = Me.GetNextControl(ctl, True)
                    If ctl.Tag = e.KeyCode Then
                        Dim Key As KeyControl = DirectCast(ctl, KeyControl)
                        e.Handled = Key.DisableHandle
                        If Key_CapsLock.Toggled = True Then
                            If Key.Text.Length = 1 Then
                                eKeyName = Key.Text.ToUpper
                            End If
                        ElseIf e.Shift = False Then
                            If Key.Text.Length = 1 Then
                                eKeyName = Key.Text
                            End If
                        Else
                            If Key.Text.Length = 1 Then
                                eKeyName = Key.TextUpper
                            End If
                            eKeyCode = Asc(Key.TextUpper)
                        End If
                        Key.State = MouseState.Down
                        Key.Invalidate()
                        KeyPressed = True
                    End If
                Catch ex As Exception
                End Try
            Loop
        Catch ex As Exception
        End Try

        If e.Shift = True Then
            eShiftDown = True
            KeyPressed = True
            ShiftWasDown = True
            Try
                Dim contrl As Control = Me.GetNextControl(Me, True)
                Do Until contrl Is Nothing
                    Try
                        contrl = Me.GetNextControl(contrl, True)
                        Dim Key As KeyControl = DirectCast(contrl, KeyControl)
                        Key.ShiftDown = True
                        Key.Invalidate()
                    Catch ex As Exception
                    End Try
                Loop
            Catch ex As Exception
            End Try
        Else
            eShiftDown = False
        End If
        For Each Hotkey As DB_Item In HotkeyDB.Items
            If Hotkey.Name = e.KeyCode Then
                e.Handled = DisableHotkeyHandles
            End If
        Next

        If KeyPressed = False Then
            Key_Other.State = MouseState.Down
        End If
        RaiseEvent KB_KeyDown(eKeyCode, eKeyName, eCtrlDown, eShiftDown)
    End Sub
    Private Sub kb_hook_KeyUp(sender As Object, e As Windows.Forms.KeyEventArgs) Handles kb_hook.KeyUp
        Dim eCtrlDown As Boolean = e.Control
        Dim eShiftDown As Boolean = e.Shift
        Dim eKeyName As String = Nothing
        Dim eKeyCode As Keys = e.KeyCode


        Dim KeyPressed As Boolean = False
        Dim ctl As Control = Me.GetNextControl(Me, True)
        Do Until ctl Is Nothing
            Try
                ctl = Me.GetNextControl(ctl, True)
                If ctl.Tag = e.KeyCode Then
                    Dim Key As KeyControl = DirectCast(ctl, KeyControl)
                    e.Handled = Key.DisableHandle
                    If Key_CapsLock.Toggled = True Then
                        If Key.Text.Length = 1 Then eKeyName = Key.Text.ToUpper
                    ElseIf e.Shift = False Then
                        If Key.Text.Length = 1 Then eKeyName = Key.Text
                    Else
                        If Key.Text.Length = 1 Then eKeyName = Key.TextUpper
                        eKeyCode = Asc(Key.TextUpper)
                    End If
                    Key.State = MouseState.None
                    KeyPressed = True
                    Key.Invalidate()
                End If
            Catch ex As Exception

            End Try
        Loop

        If ShiftWasDown = True And e.Shift = False Then
            KeyPressed = True
            ShiftWasDown = False
            Try
                Dim contrl As Control = Me.GetNextControl(Me, True)
                Do Until contrl Is Nothing
                    Try
                        contrl = Me.GetNextControl(contrl, True)
                        Dim Key As KeyControl = DirectCast(contrl, KeyControl)
                        Key.ShiftDown = False
                        Key.Invalidate()
                    Catch ex As Exception
                    End Try
                Loop
            Catch ex As Exception
            End Try
        End If

        If KeyPressed = False Then
            Key_Other.State = MouseState.None
        End If
        If e.KeyCode = Keys.CapsLock Or e.KeyCode = Keys.NumLock Then
            Dim Thread As New Threading.Thread(AddressOf SetToggles)
            Thread.Start()
        End If
        For Each Hotkey As DB_Item In HotkeyDB.Items
            If Hotkey.Name = e.KeyCode Then
                e.Handled = DisableHotkeyHandles
                Execute(_RegisteredMethods.Item(HotkeyDB.SearchItems(e.KeyCode)(0).Parameters(0)), HotkeyDB.SearchItems(e.KeyCode)(0).Parameters(0), Nothing)
            End If
        Next
        RaiseEvent KB_KeyUp(eKeyCode, eKeyName, eCtrlDown, eShiftDown)
    End Sub
    Private Sub DetailForm_KeyBind_Changed(Bind As DB_Item) Handles DetailForm.KeyBind_Changed
        If HotkeyDB.SearchItems(Bind.Name).Count <> 0 Then HotkeyDB.DeleteItem(Bind.Name)
        HotkeyDB.AddItem(Bind)
    End Sub
    Private Sub DetailForm_KeyBind_HandleChanged(ID As Integer, State As Boolean) Handles DetailForm.KeyBind_HandleChanged
        FindKey(ID).DisableHandle = State
        If DisabledKeys.SearchItem(ID) IsNot Nothing Then DisabledKeys.DeleteItem(ID)
        DisabledKeys.AddItem(New DB_Item(ID, New List(Of String) From {State}))
    End Sub
    Private Function FindKey(ByVal KeyCode As Integer) As KeyControl
        Try
            Dim contrl As Control = Me.GetNextControl(Me, True)
            Do Until contrl Is Nothing
                Try
                    contrl = Me.GetNextControl(contrl, True)
                    Dim Key As KeyControl = DirectCast(contrl, KeyControl)
                    If Key.Tag = KeyCode Then
                        Return Key
                    End If
                Catch ex As Exception
                End Try
            Loop
        Catch ex As Exception
        End Try
        Return Nothing
    End Function
    Private Sub DetailForm_KeyBind_Deleted(Bind As DB_Item) Handles DetailForm.KeyBind_Deleted
        If HotkeyDB.SearchItems(Bind.Name).Count <> 0 Then HotkeyDB.DeleteItem(Bind.Name)
    End Sub
    Private Sub KeyControl_MouseDown(sender As Object, e As MouseEventArgs)
        If e.Button = Windows.Forms.MouseButtons.Left And sender.tag <> Keys.CapsLock And sender.tag <> Keys.NumLock Then
            InvokeKeydown(sender.tag)
        End If
    End Sub
    Private Sub KeyControl_MouseUp(sender As Object, e As MouseEventArgs)
        If e.Button = Windows.Forms.MouseButtons.Right Then
            If DetailForm IsNot Nothing Then DetailForm.Close()
            DetailForm = New frmDetail
            Dim Key As KeyControl = TryCast(sender, KeyControl)
            DetailForm.KeyControl1.Text = Key.Text
            DetailForm.KeyControl1.TextUpper = Key.TextUpper
            DetailForm.KeyControl1.DisplayType = Key.DisplayType
            If Key.Image IsNot Nothing Then
                DetailForm.KeyControl1.Image = Key.ImageUp
                DetailForm.KeyControl1.ImageUp = Key.ImageUp
                DetailForm.KeyControl1.ImageDown = Key.ImageDown
            End If
            DetailForm.KeyControl1 = Key
            If HotkeyDB.SearchItems(Key.Tag).Count <> 0 Then DetailForm.SelectedMethod = HotkeyDB.SearchItems(Key.Tag)(0).Parameters(0)
            DetailForm.Methods = _RegisteredMethods
            DetailForm.Show()
        ElseIf e.Button = Windows.Forms.MouseButtons.Left And sender.tag <> Keys.CapsLock Then
            InvokeKeyup(sender.tag)
        End If
    End Sub
    Private Sub CapsLock_Click(sender As Object, e As EventArgs) Handles Key_CapsLock.Click
        If Key_CapsLock.Toggled = False Then
            SetCapsLock(True)
        Else
            SetCapsLock(False)
        End If
    End Sub
    Private Sub NumLock_Click(sender As Object, e As EventArgs) Handles Key_NumLock.Click
        If Key_NumLock.Toggled = False Then
            SetNumLock(True)
        Else
            SetNumLock(False)
        End If
    End Sub
    Private Sub Key_Other_Click(sender As Object, e As EventArgs) Handles Key_Other.Click

    End Sub
End Class
